<?php

/**
 * CCoreMimeStreamWrapper
 *
 * @author Usenko Timur <usenko.timur@gmail.com>
 * @package mime
 */

CCore::Using('mime.utils');

/**
 * @package mime
 */
class CCoreMimeStreamWrapper
{
	/**
	 * @var array
	 */
	static $_aParentStream = array();

	/**
	 * @var resourse
	 */
	private $_rStream;

	/**
	 * @var int
	 */
	private $_iPos;

	/**
	 * @var string
	 */
	private $_sBuffer;

	/**
	 * @var string
	 */
	private $_sReadEndBuffer;

	/**
	 * @var string
	 */
	private $_sFunctionName;

	/**
	 * @var int
	 */
	private $_iBufferSize;

	/**
	 * @var str
	 */
	private $_sFromEncoding;

	/**
	 * @var str
	 */
	private $_sToEncoding;

	/**
	 * @param resource $rStream
	 * @param int $iLiteralLen
	 */
	public static function CreateBinaryStream($rStream, $sUtilsDecodeFunctionName = null, $sFromEncoding = null, $sToEncoding = null)
	{
		if (!in_array('ccorebinarybody', stream_get_wrappers()))
		{
			stream_wrapper_register('ccorebinarybody', 'CCoreMimeStreamWrapper');
		}

		if (null === $sUtilsDecodeFunctionName)
		{
			$sUtilsDecodeFunctionName = 'InlineNullDecode';
		}

		if (null !== $sFromEncoding && null !== $sToEncoding)
		{
			self::$_aParentStream[] = self::CreateBinaryStream($rStream, $sUtilsDecodeFunctionName);
			return fopen('ccorebinarybody://InlineConvertDecode/?id='.count(self::$_aParentStream).'&from='.$sFromEncoding.'&to='.$sToEncoding, 'rb');
		}
		else
		{
			self::$_aParentStream[] = $rStream;
			return fopen('ccorebinarybody://'.$sUtilsDecodeFunctionName.'/?id='.count(self::$_aParentStream), 'rb');
		}
	}
	
	/**
	 * @param string $sPath
	 * @param string $sMode
	 * @param int $iOptions
	 * @param string &$sOpenedPath
	 * @return bool
	 */
	public function stream_open($sPath, $sMode, $iOptions, &$sOpenedPath)
	{
		$bResult = false;
		$aPath = parse_url($sPath);
		if (isset($aPath['host']) && !empty($aPath['host']))
		{
			$this->_rStream = null;
			$this->_sFromEncoding = null;
			$this->_sToEncoding = null;

			$this->_sFunctionName = $aPath['host'];

			if (isset($aPath['query']) && !empty($aPath['query']))
			{
				$aOutput = array();
				parse_str($aPath['query'], $aOutput);
				if (isset($aOutput['id']))
				{
					$iId = (int) $aOutput['id'];
					if (isset(self::$_aParentStream[--$iId]))
					{
						$this->_rStream = self::$_aParentStream[$iId];
					}
				}
				if (isset($aOutput['from']))
				{
					$this->_sFromEncoding = $aOutput['from'];
				}
				if (isset($aOutput['to']))
				{
					$this->_sToEncoding = $aOutput['to'];
				}
			}

			$this->_sBuffer = '';
			$this->_sReadEndBuffer = '';
			$this->_iPos = 0;
			$this->_iBufferSize = 8192;
			$bResult = is_resource($this->_rStream);
		}
		return $bResult;
	}

	/**
	 * @param int $iCount
	 * @return string
	 */
	public function stream_read($iCount)
	{
		$sReturn = '';
		$sFunctionName = $this->_sFunctionName;
		if ($iCount > 0)
		{
			if ($iCount < strlen($this->_sBuffer))
			{
				$sReturn = substr($this->_sBuffer, 0, $iCount);
				$this->_sBuffer = substr($this->_sBuffer, $iCount);
			}
			else
			{
				$sReturn = $this->_sBuffer;
				while ($iCount > 0)
				{
					if (feof($this->_rStream))
					{
						if (0 === strlen($this->_sBuffer.$sReturn))
						{
							return false;
						}
						
						if (0 < strlen($this->_sReadEndBuffer))
						{
							$sReturn .= CCoreMimeUtils::$sFunctionName($this->_sReadEndBuffer, 
								$this->_sReadEndBuffer, $this->_sFromEncoding, $this->_sToEncoding);
							$iDecodeLen = strlen($sReturn);
						}

						$iCount = 0;
						$this->_sBuffer = '';
					}
					else
					{
						$sReadResult = fread($this->_rStream, $this->_iBufferSize);
						if (false === $sReadResult)
						{
							return false;
						}

						$sReturn .= CCoreMimeUtils::$sFunctionName($this->_sReadEndBuffer.$sReadResult,
							$this->_sReadEndBuffer, $this->_sFromEncoding, $this->_sToEncoding);
						$iDecodeLen = strlen($sReturn);
						if ($iCount < $iDecodeLen)
						{
							$this->_sBuffer = substr($sReturn, $iCount);
							$sReturn = substr($sReturn, 0, $iCount);
							$iCount = 0;
						}
						else
						{
							$iCount -= $iDecodeLen;
						}
					}
				}
			}

			$this->_iPos += strlen($sReturn);
			return $sReturn;
		}
		return false;
	}

	/**
	 * @param string $sData
	 * @return int
	 */
	public function stream_write($sData)
	{
		return 0;
	}

	/**
	 * @return int
	 */
	public function stream_tell()
	{
		return $this->_iPos;
	}

	/**
	 * @return bool
	 */
	public function stream_eof()
	{
		return empty($this->_sBuffer) && feof($this->_rStream);
	}

	/**
	 * @return array
	 */
	public function stream_stat()
	{
		return array(
			'dev' => 2,
			'ino' => 0,
			'mode' => 33206,
			'nlink' => 1,
			'uid' => 0,
			'gid' => 0,
			'rdev' => 2,
			'size' => 0,
			'atime' => 1061067181,
			'mtime' => 1056136526,
			'ctime' => 1056136526,
			'blksize' => -1,
			'blocks' => -1
		);
	}

	/**
	 * @param int $iOffset
	 * @param int $iWhence
	 * @return bool
	 */
	public function stream_seek($iOffset, $iWhence)
	{
		if (0 === $iOffset && SEEK_SET === $iWhence)
		{
			fseek($this->_rStream, 0, SEEK_SET);
			$this->_sBuffer = '';
			$this->_sReadEndBuffer = '';
			$this->_iPos = 0;
			return true;
		}
		return false;
	}
}
